/** @file    ShellCommand.cpp
 *  @author  Alessandro Polo
 *  @version $Id: ShellCommand.cpp 2832 2010-08-02 22:53:46Z alex $
 *  @brief
 * File containing methods for the wosh::communication::ShellCommand class.
 * The header for this class can be found in ShellCommand.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

 #include <framework/communication/ShellCommand.h>

 #include <core/Utilities.h>
 #include <core/Request.h>
 #include <core/List.h>
 #include <core/Table.h>
 #include <stdio.h>

using namespace std;
namespace wosh {
 namespace communication {

/*
COMMAND ARG1 "ARG2 ignore spaces, considered string" ARG3
COMMAND "ARG1 ignore spaces, considered string"
*/
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Table* ShellCommand::toArgumentTable() const {
	Table* tbl = new Table(this->arguments.size(), 2);
	unsigned int iRow = 0;
	std::vector<ShellCommandArg>::const_iterator it;
	for (it=this->arguments.begin(); it!=this->arguments.end(); ++it, ++iRow ) {
		tbl->set<Variant>( (*it).field, iRow, 0);
		tbl->set( (*it).argument.clone(), iRow, 1);
	 }
	return tbl;
 }

List* ShellCommand::toArgumentList() const {
	List* args = new List(this->arguments.size());
	std::vector<ShellCommandArg>::const_iterator it;
	for (it=this->arguments.begin(); it!=this->arguments.end(); ++it ) {
		args->push_back( (*it).argument.clone());
	 }
	return args;
 }

Request* ShellCommand::toRequest() const {
	Request* request = new Request(this->command);

	if ( this->hasFields ) {
		request->setData( toArgumentTable() );
	 }
	else if ( !this->arguments.empty() ) {
		if ( this->arguments.size() == 1 )
			request->setData( this->arguments.front().argument.clone() );
		else
			request->setData( toArgumentList() );
	 }
	return request;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

const Variant& ShellCommand::getArgument( int index ) const {
	if ( index < 0 || index >= (int)this->arguments.size() )
		return Variant::EmptyVariant;
	return this->arguments.at(index).argument;
 }

const Variant& ShellCommand::getArgument( const string& field ) const {
	std::vector<ShellCommandArg>::const_iterator it;
	for (it=this->arguments.begin(); it!=this->arguments.end(); ++it ) {
		if ( (*it).field != field ) continue;
		return (*it).argument;
	 }
	return Variant::EmptyVariant;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

ShellCommand& ShellCommand::operator=( const ShellCommand& m ) {
	if (this == &m) return *this; // same object?
	this->command = m.command;
	this->arguments = m.arguments;
	return *this;
 }

bool ShellCommand::operator==( const ShellCommand& other ) const {
	if ( this->command != other.command )
		return false;
	if ( this->arguments != other.arguments )
		return false;
	return true;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void ShellCommand::clear() {
	this->command = "";
	this->arguments.clear();
	this->hasFields = false;
 }

bool ShellCommand::fromString( const std::string& dataline )
 {
	clear();
	if ( dataline.empty() || dataline.find(" ") == std::string::npos ) {
		this->command = dataline;
		return true;
	 }
	std::string::size_type cPos = 0;
	std::string::size_type cPosNext = 0;
	while ( cPos < dataline.size() && (dataline.at(cPos) == ' ' || dataline.at(cPos) == '\t') ) {
		++cPos;
	 }
	cPosNext = dataline.find(" ", cPos);
	if ( cPosNext == std::string::npos ) {
		this->command = dataline.substr(cPos);
		return true;
	 }
	this->command = dataline.substr(cPos, cPosNext-cPos);
	// so we have an argument string beginning at cPosNext? remove white spaces!
	cPos = cPosNext;
	while ( cPos < dataline.size() && (dataline.at(cPos) == ' ' || dataline.at(cPos) == '\t') ) {
		++cPos;
	 }
	// so we have an argument string beginning at cPos
	cPosNext = cPos -1;
	std::string cField = "";
	std::string buffer = "";
	bool quote_opened = false;
	while (true) {
		cPos = cPosNext + 1;
		cPosNext = dataline.find("\"", cPos);
		// quote not found:
		if ( cPosNext == std::string::npos ) {
			buffer = dataline.substr(cPos);
			if ( quote_opened && !buffer.empty() ) { // consider it as last missing char..
				ShellCommandArg arg;
				arg.field = cField; // eventually associate last (current) field to the value
				arg.argument.fromString(buffer);
				this->arguments.push_back(arg);
				cField = "";
			 }
			else // no quotes in the dataline
				parseArgs(buffer, cField);
			break;
		 }
		else { // quote found
			buffer = dataline.substr(cPos, cPosNext-cPos);
			if ( quote_opened ) { // its the closing quote => it was a plain string or value
				ShellCommandArg arg;
				arg.field = cField; // eventually associate last (current) field to the value
				arg.argument.fromString(buffer);
				this->arguments.push_back(arg);
			 }
			else { // it's the opening quote, evaluating: new arguments or value (of previous field)
				parseArgs(buffer, cField);
				// now we will parse next quoted value, as new argument or value of field
				// eventually updated by parseArgs()
			 }
			quote_opened = !quote_opened;
		 }
	}

//printf("cmd: %s\n", this->command.c_str() );
//std::vector<ShellCommandArg>::const_iterator it;
//int i = 0;
//for (it=this->arguments.begin(); it!=this->arguments.end(); ++it, ++i ) {
//	printf("arg%d: '%s'='%s' [%s]\n", i, this->arguments.at(i).field.c_str(), this->arguments.at(i).argument.toString().c_str(), this->arguments.at(i).argument.getTypeAsString() );
// }

	return !quote_opened;
 }

void ShellCommand::parseArgs( const std::string& args, std::string& current_field ) {
	if ( args.find("\"") != std::string::npos ) {
printf("EEEERRORRR ShellCommand::parseArgs \n");
		return;
	 }
	vector<string> temp;
	if ( args.find("--") == std::string::npos ) {
 		Utilities::splitString(args, " ", temp, false, true);
		std::vector<string>::const_iterator it;
		for (it=temp.begin(); it!=temp.end(); ++it ) {
			ShellCommandArg arg;
			arg.argument.fromString(*it);
			this->arguments.push_back(arg);
		 }
		current_field = "";
		return;
	 }
	else {
 		this->hasFields = true;
		Utilities::splitString(args, "--", temp, false, true);
		std::string::size_type eqPos = 0;
		// pairs of FIELD=VALUE
		std::vector<string>::const_iterator it;
		for (it=temp.begin(); it!=temp.end(); ++it ) {
			if ( (*it).empty() ) continue; // error
			ShellCommandArg arg;
			eqPos = (*it).find("=");
			if ( eqPos == std::string::npos ) { // field considered as value!
				arg.argument.fromString(args);
			 }
			else if ( eqPos+1 == (*it).size() ) { // value missing, probably is in the next token as quoted string!
				current_field = (*it).substr(0,eqPos);
				if ( it+1 == temp.end() )
					break;
			 }
			else {
				arg.field = (*it).substr(0,eqPos);
				arg.argument.fromString( (*it).substr(eqPos+1) );
			 }
			this->arguments.push_back(arg);
		 }
	 }
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

std::string ShellCommand::toString( bool include_command ) const {
	std::string temp;
	if ( include_command )
		temp = this->command + " ";

	std::vector<ShellCommandArg>::const_iterator it;
	for (it=this->arguments.begin(); it!=this->arguments.end(); ++it ) {
		if ( !(*it).argument.isStringNotEmpty() ) {
			if ( !(*it).field.empty() )
				temp += Utilities::format(" --%s=%s", (*it).field.c_str(), (*it).argument.toString().c_str() );
			else
				temp += Utilities::format(" %s", (*it).argument.toString().c_str() );
		 }
		else {
			if ( !(*it).field.empty() )
				temp += Utilities::format(" --%s=", (*it).field.c_str() );
			else
				temp += " ";
			if ( (*it).argument.asString().find(" ") != std::string::npos )
				temp += (*it).argument.asString();
			else
				temp += Utilities::format("\"%s\"", (*it).argument.asString().c_str() );
		 }
	 }
	return temp;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

 }; // namespace communication
}; // namespace wosh
